home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2007 December / PCWKCD1207B.iso / Blogowanie poza sfera / Flock 1.0 beta / flock-1.0RC3.en-US.win32.exe / flock / components / flockSearchvideoService.js < prev    next >
Text File  |  2007-10-18  |  21KB  |  564 lines

  1. // BEGIN FLOCK GPL
  2. // 
  3. // Copyright Flock Inc. 2005-2007
  4. // http://flock.com
  5. // 
  6. // This file may be used under the terms of of the
  7. // GNU General Public License Version 2 or later (the "GPL"),
  8. // http://www.gnu.org/licenses/gpl.html
  9. // 
  10. // Software distributed under the License is distributed on an "AS IS" basis,
  11. // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12. // for the specific language governing rights and limitations under the
  13. // License.
  14. // 
  15. // END FLOCK GPL
  16.  
  17. const Cc = Components.classes;
  18. const Ci = Components.interfaces;
  19. const Cr = Components.results;
  20.  
  21. const ENABLE_DEBUG = true;
  22. function DEBUG(x) { if (ENABLE_DEBUG) debug("flockSearchvideoService: "+x+"\n"); }
  23.  
  24. const SEARCHVIDEO_TITLE                  = "Truveo Web Service";
  25. const SEARCHVIDEO_FAVICON                = "chrome://flock/content/services/searchvideo/favicon.png";
  26. const SEARCHVIDEO_CID                    = Components.ID("{31def21e-02d6-4d9a-848c-2c1895e50626}");
  27. const SEARCHVIDEO_CONTRACTID             = "@flock.com/?photo-api-searchvideo;1";
  28. const APPID                              = "1x1jhj64466mi12ia";
  29. const CATEGORY_ENTRY_NAME                = "searchvideo";
  30. const SERVICE_ENABLED_PREF               = "flock.service.searchvideo.enabled";
  31.  
  32. const SEARCHVIDEO_STRING_BUNDLE = "chrome://flock/locale/services/searchvideo.properties";
  33.  
  34. var gCompTK;
  35. var alreadySearched = false;
  36. function getCompTK() {
  37.   if (!gCompTK) {
  38.     gCompTK = Cc["@flock.com/singleton;1"].getService(Ci.flockISingleton)
  39.                 .getSingleton("chrome://browser/content/flock/services/common/load-compTK.js").wrappedJSObject;
  40.   }
  41.   return gCompTK;
  42. }
  43.  
  44.  
  45. function loadLibraryFromSpec(aSpec)
  46. {
  47.   var loader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
  48.                .getService(Ci.mozIJSSubScriptLoader);
  49.   loader.loadSubScript(aSpec);
  50. }
  51. loadLibraryFromSpec("chrome://browser/content/flock/photo/photoAPI.js");
  52.  
  53. function searchvideoPhoto() {}
  54.  
  55. searchvideoPhoto.prototype = {
  56.   id: "",
  57.   thumbnail: "",
  58.   webPageUrl: "",
  59.   midSizePhoto: "",
  60.   largeSizePhoto: "",
  61.   title: "",
  62.   username: "",
  63.   userid: "",
  64.   embedTag: "",
  65.   is_public: "true",
  66.   is_video: "true",
  67.   svcShortName: 'searchvideo',
  68.   has_miniView: "true",
  69.   buildTooltip: function( ) {
  70.     // do we have to use document from the window to ceate elements? -- ja
  71.     var wm = Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator);
  72.     var win = wm.getMostRecentWindow('navigator:browser');
  73.     if (!win) return null;
  74.  
  75.     var hbox = win.document.createElement('hbox');
  76.     var img = win.document.createElement('image');
  77.     img.setAttribute('src', this.icon );
  78.     hbox.appendChild(img);
  79.     var box = win.document.createElement('vbox');
  80.     box.setAttribute('style', 'max-width: 300px');
  81.     var title = win.document.createElement('label');
  82.     title.setAttribute('value', this.title );
  83.     title.setAttribute('crop', 'end');
  84.     box.appendChild(title);
  85.     var lbl = win.document.createElement('label');
  86.     lbl.setAttribute('value', this.username );
  87.     lbl.setAttribute('class', 'user');
  88.     box.appendChild(lbl);
  89.     hbox.appendChild(box)
  90.  
  91.     var vbox = win.document.createElement('vbox');
  92.     var cbox = win.document.createElement('cbox');
  93.     var largeImg = win.document.createElement('image');
  94.     largeImg.setAttribute('src', this.midSizePhoto);
  95.     largeImg.setAttribute('style', 'margin-bottom: 2px;');
  96.     var spacer = win.document.createElement('spacer');
  97.     spacer.setAttribute('flex', '1');
  98.     cbox.appendChild(largeImg);
  99.     cbox.appendChild(spacer);
  100.     vbox.appendChild(cbox);
  101.     vbox.appendChild(hbox);
  102.  
  103.     return vbox;
  104.   },
  105.   buildHTML: function ( ) {
  106.     const EMBED_TAG = "</embed>";
  107.     var embedTagCopy = this.embedTag;
  108.  
  109.     // add a 'value' to the EMBED tag
  110.     // since this.embedTag is a string, we need to insert the value in the
  111.     // appropriate position so as not to break the markup
  112.     // TODO JVL: find RegExp implementation
  113.     if (embedTagCopy.lastIndexOf(EMBED_TAG) == (embedTagCopy.length - EMBED_TAG.length)) {
  114.       // case 1: <embed></embed>
  115.       embedTagCopy = embedTagCopy.slice(0, embedTagCopy.length - EMBED_TAG.length);
  116.       embedTagCopy += this.webPageUrl;
  117.       embedTagCopy += EMBED_TAG;
  118.     } else if (embedTagCopy.lastIndexOf("/>") == (embedTagCopy.length - 2)) {
  119.       // case 2: <embed/>
  120.       embedTagCopy = embedTagCopy.slice(0, embedTagCopy.length - 2);
  121.       embedTagCopy += ">";
  122.       embedTagCopy += this.webPageUrl;
  123.       embedTagCopy += EMBED_TAG;
  124.     } else if (embedTagCopy.lastIndexOf(">") == (embedTagCopy.length - 1)) {
  125.       // case 3: <embed>
  126.       embedTagCopy += this.webPageUrl;
  127.       embedTagCopy += EMBED_TAG;
  128.     }
  129.     return embedTagCopy;
  130.   },
  131.   buildBBCode: function ( ) {
  132.     return '[url=' + this.webPageUrl + '][img]'+ this.midSizePhoto +'[/img][/url]';
  133.   },
  134.   buildMiniPage: function ( ) {
  135.     return this.webPageUrl;
  136.   },
  137.   QueryInterface: function(iid) {
  138.     if (!iid.equals(Ci.nsISupports) &&
  139.         !iid.equals(Ci.flockIPhoto)) {
  140.       throw Cr.NS_ERROR_NO_INTERFACE;
  141.     }
  142.     return this;
  143.   }
  144. };
  145.  
  146.  
  147. searchvideoPhoto.prototype.__defineGetter__('metaData', function () {
  148.   var metaData = Components.classes["@mozilla.org/hash-property-bag;1"].createInstance(Components.interfaces.nsIWritablePropertyBag2);
  149.   metaData.setPropertyAsAString("title", this.title);  
  150.   metaData.QueryInterface(Components.interfaces.nsIPropertyBag);
  151.   
  152.   // expose other metadata props here, then you can display them with custom xbls
  153.   // which extend from the generic photo binding
  154.   
  155.   return metaData;
  156. })
  157. // ====================================================
  158. // ========== BEGIN searchvideoService class ==========
  159. // ====================================================
  160.  
  161. function searchvideoService() {
  162.   this.url = "http://www.searchvideo.com";
  163.   this._ctk = {
  164.     interfaces: [
  165.       "nsISupports",
  166.       "nsIClassInfo",
  167.       "nsISupportsCString",
  168.       "nsIObserver",
  169.       "flockIWebService",
  170.       "flockIMediaWebService",
  171.     ],
  172.     shortName: "searchvideo",
  173.     fullName: "Truveo",
  174.     description: SEARCHVIDEO_TITLE,
  175.     favicon: SEARCHVIDEO_FAVICON,
  176.     CID: SEARCHVIDEO_CID,
  177.     contractID: SEARCHVIDEO_CONTRACTID
  178.   };
  179.  
  180.   var sbs = Cc["@mozilla.org/intl/stringbundle;1"]
  181.             .getService(Ci.nsIStringBundleService);
  182.   var bundle = sbs.createBundle(SEARCHVIDEO_STRING_BUNDLE);
  183.  
  184.   this._channels = {
  185.     "special:all": {
  186.       title: bundle.GetStringFromName("flock.searchvideo.title.all"),
  187.       supportsSearch: true
  188.     },
  189.     "special:topfav": {
  190.       title: bundle.GetStringFromName("flock.searchvideo.title.topfav"),
  191.       supportsSearch: true
  192.     },
  193.     "special:pop": {
  194.       title: bundle.GetStringFromName("flock.searchvideo.title.pop"),
  195.       supportsSearch: true
  196.     },
  197.     "special:abc": {
  198.       title: bundle.GetStringFromName("flock.searchvideo.title.abc"),
  199.       supportsSearch: true
  200.     },
  201.     "special:myspace": {
  202.       title: bundle.GetStringFromName("flock.searchvideo.title.myspace"),
  203.       supportsSearch: true
  204.     },
  205.     "special:disney": {
  206.       title: bundle.GetStringFromName("flock.searchvideo.title.disney"),
  207.       supportsSearch: true
  208.     },
  209.     "special:bbcnews": {
  210.       title: bundle.GetStringFromName("flock.searchvideo.title.bbcnews"),
  211.       supportsSearch: true
  212.     }
  213.   };
  214.  
  215.   this.webDetective = Cc["@flock.com/account-utils;1"].getService(Ci.flockIAccountUtils).useWebDetective("searchvideo.xml");
  216.   this.faves_coop = Cc["@flock.com/singleton;1"]
  217.                     .getService(Ci.flockISingleton)
  218.                     .getSingleton("chrome://flock/content/common/load-faves-coop.js")
  219.                     .wrappedJSObject;
  220.   this._logger = Cc['@flock.com/logger;1'].createInstance(Ci.flockILogger);
  221.   this._logger.init('searchvideo');
  222.  
  223.   this.prefService = Components.classes["@mozilla.org/preferences-service;1"]
  224.                                .getService(Components.interfaces.nsIPrefBranch);
  225.   if (this.prefService.getPrefType(SERVICE_ENABLED_PREF) &&
  226.       !this.prefService.getBoolPref(SERVICE_ENABLED_PREF))
  227.   {
  228.     this._logger.debug("Pref "+SERVICE_ENABLED_PREF+" set to FALSE... not initializing.");
  229.     var catMgr = Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager);
  230.     catMgr.deleteCategoryEntry("flockMediaProvider", CATEGORY_ENTRY_NAME, true);
  231.     return; 
  232.   }
  233. }
  234.  
  235. searchvideoService.prototype.serviceName = "Truveo";
  236. searchvideoService.prototype.shortName = "searchvideo";
  237. searchvideoService.prototype.iconUrl = SEARCHVIDEO_FAVICON;
  238. searchvideoService.prototype.count = 1;
  239.  
  240.  
  241. searchvideoService.prototype.getPhotoFromRDFNode = 
  242. function (aRDFId)
  243. {
  244.   var newPhoto = new searchvideoPhoto();
  245.   var coopPhoto = this.faves_coop.get(aRDFId);
  246.   newPhoto.webPageUrl = coopPhoto.URL;
  247.   newPhoto.thumbnail = coopPhoto.thumbnail;
  248.   newPhoto.midSizePhoto = coopPhoto.midSizePhoto;
  249.   newPhoto.largeSizePhoto = coopPhoto.largeSizePhoto;
  250.   newPhoto.username = coopPhoto.username;
  251.   newPhoto.userid = coopPhoto.userid;
  252.   newPhoto.title = coopPhoto.name;
  253.   newPhoto.id = coopPhoto.photoid;
  254.   newPhoto.icon = coopPhoto.favicon;
  255.   newPhoto.uploadDate = coopPhoto.datevalue;
  256.   newPhoto.is_public = coopPhoto.is_public;
  257.   newPhoto.is_video = coopPhoto.is_video;
  258.   return newPhoto;
  259. }
  260.  
  261. searchvideoService.prototype.handlePhotosResult =
  262. function (aXML, aUserid) {
  263.   var rval = new Array();
  264.   var response = aXML.getElementsByTagName("VideoSet")[0];
  265.   var videoList = aXML.getElementsByTagName("Video");
  266.   for (var i = 0; i < videoList.length; i++) {
  267.     var video = videoList[i];
  268.  
  269.     var newPhoto = new searchvideoPhoto();
  270.     newPhoto.id = video.getElementsByTagName("id")[0].textContent;
  271.     newPhoto.title = video.getElementsByTagName("title")[0].textContent;
  272.     newPhoto.webPageUrl = video.getElementsByTagName("videoUrl")[0].textContent;
  273.     newPhoto.thumbnail = video.getElementsByTagName("thumbnailUrl")[0].textContent;
  274.     var largeThumbnail = video.getElementsByTagName("thumbnailUrlLarge");
  275.     if (largeThumbnail.length > 0) {
  276.       newPhoto.midSizePhoto = largeThumbnail[0].textContent;
  277.       newPhoto.largeSizePhoto = largeThumbnail[0].textContent;
  278.     }
  279.     else {
  280.       newPhoto.midSizePhoto = newPhoto.thumbnail;
  281.       newPhoto.largeSizePhoto = newPhoto.thumbnail;
  282.     }
  283.     newPhoto.username = video.getElementsByTagName("channel")[0].textContent;
  284.     newPhoto.userid = video.getElementsByTagName("channelUrl")[0].textContent;
  285.     var dateProduced = video.getElementsByTagName("dateProduced");
  286.     if (dateProduced.length > 0)
  287.       newPhoto.uploadDate = dateProduced[0].textContent;
  288.     else
  289.       newPhoto.uploadDate = video.getElementsByTagName("dateFound")[0].textContent;
  290.     newPhoto.is_public = true;
  291.     newPhoto.is_video = true;
  292.     newPhoto.embedTag = video.getElementsByTagName("videoResultEmbedTag")[0].textContent;
  293.     rval.push(newPhoto);
  294.   }
  295.   return rval;
  296. }
  297.  
  298. searchvideoService.prototype.queryChannel =
  299. function searchvideoService_queryChannel(aListener, aQueryString, aCount, aPage) {
  300.   var aQuery = new queryHelper(aQueryString);
  301.   var inst=this;
  302.   var myListener = {
  303.     onResult: function (aXML) {
  304.       var rval = inst.handlePhotosResult(aXML);
  305.       var enum_ = {
  306.         hasMoreElements: function() {
  307.           return (rval.length>0);
  308.         },
  309.         getNext: function() {
  310.           return rval.shift();
  311.         }
  312.       }
  313.       aListener.onSearchResult(enum_);
  314.     },
  315.     onError: function (aError) {
  316.       aListener.onError(aError);
  317.     }
  318.   }
  319.  
  320.   var params = {
  321.     results: (aCount ? aCount : 50),
  322.     start: (aPage ? aPage : 1)
  323.   };
  324.  
  325.   switch (aQuery.special) {
  326.     case "recent": params.query = "sort:mostRecent "; break;
  327.     case "topfav": params.query = "sort:topFavorites "; break;
  328.     case "pop": params.query = "sort:mostPopular "; break;
  329.     case "poptoday": params.query = "sort:mostPopularToday "; break;
  330.     case "popweek": params.query = "sort:mostPopularThisWeek "; break;
  331.     case "popmonth": params.query = "sort:mostPopularThisMonth "; break;
  332.     case "abc": params.query = "sort:mostPopular channel:ABC "; break;
  333.     case "myspace": params.query = "sort:mostPopular channel:MYSPACE "; break;
  334.     case "disney": params.query = "sort:mostPopular channel:Disney "; break;
  335.     case "bbcnews": params.query = "sort:mostPopular channel:\"BBC News\""; break;
  336.     default: params.query = "sort:vrank ";
  337.   }
  338.  
  339.   if (aQuery.search)
  340.        params.query += aQuery.search;
  341.  
  342.   this.call(myListener, "truveo.videos.getVideos", params);
  343. }
  344.  
  345. searchvideoService.prototype.findByUsername =
  346. function searchvideoService_findByUsername(aListener, aUsername) {
  347.   aListener.onError(null);
  348. }
  349.  
  350. searchvideoService.prototype.search =
  351. function searchvideoService_search(aListener, aQueryString, aCount, aPage) {
  352.   
  353.   //this.count = this.count+aCount;
  354.   // Searchvideo only supports channel queries for now
  355.   if(aPage == 1) this.count = 1;
  356.   this.queryChannel(aListener, aQueryString, aCount, (aPage ? this.count : 1));
  357.   this.count = aCount + this.count;
  358. }
  359.  
  360. searchvideoService.prototype.supportsSearch =
  361. function searchvideoService_supportsSearch( aQueryString ) {
  362.   var aQuery = new queryHelper(aQueryString);
  363.   
  364.   if (aQuery.special) {
  365.     var channel = this._channels["special:" + aQuery.special];
  366.     if (channel) {
  367.       return channel.supportsSearch;
  368.     }
  369.   }
  370.  
  371.   if (aQuery.search) return false;
  372.   if (aQuery.user) return true;
  373.   return false;
  374. }
  375.  
  376. searchvideoService.prototype.getError =
  377. function searchvideoService_getError (aErrorType, aXML, aHTTPErrorCode) {
  378.   var error = Components.classes["@flock.com/error;1"].createInstance(Ci.flockIError);
  379.   if  (aErrorType == "HTTP_ERROR") {
  380.     error.errorCode = aHTTPErrorCode;
  381.   } else if (aErrorType == "SERVICE_ERROR") {
  382.     var errorCode;
  383.     var errorMessage;
  384.     var serviceErrorMessage;
  385.     try {
  386.       errorCode = aXML.getElementsByTagName("Error")[0].getAttribute('Code');
  387.       serviceErrorMessage = aXML.getElementsByTagName("error")[0].textContent;
  388.     } catch (ex) {
  389.       errorCode = "999" // in case the error xml is invalid
  390.     }
  391.  
  392.     switch (errorCode) {
  393.       case "1":  // API key missing (should never happen, but...)
  394.       case "14": // API key invalid
  395.       case "15": // API key over the daily query limit (I hope it will never happen)
  396.         error.errorCode = error.PHOTOSERVICE_INVALID_API_KEY;
  397.       break;
  398.  
  399.       case "2": // A method was not submitted with the request
  400.       case "3": // The query parameter was not submitted with the request
  401.       case "4": // The results parameter must be an integer between 1 and 50
  402.       case "5": // The query parameter was not submitted with the request
  403.       case "7": // The showAdult parameter must be 0 or 1
  404.       case "8": // The tagresults parameter must be an integer between 1 and 50
  405.       case "9": // The channelresults parameter must be an integer between 1 and 50
  406.       case "10": // The categoryresults parameter must be an integer between 1 and 50
  407.       case "11": // The userresults parameter must be an integer between 1 and 50
  408.       case "13": // The method you submitted with the request was not valid
  409.       case "21": // The showRelatedItems parameter must be 0 or 1
  410.         error.errorCode = error.PHOTOSERVICE_INVALID_QUERY;
  411.       break;
  412.  
  413.       case "12":
  414.         error.errorCode = error.PHOTOSERVICE_UNAVAILABLE;
  415.       break;
  416.  
  417.       case "999":
  418.         error.errorCode = error.PHOTOSERVICE_UNKNOWN_ERROR;
  419.       break;
  420.  
  421.       default:
  422.         error.errorCode = error.PHOTOSERVICE_UNKNOWN_ERROR;
  423.       break;
  424.     }
  425.   }
  426.   error.serviceErrorCode = errorCode;
  427.   error.serviceErrorString = serviceErrorMessage;
  428.   this._logger.error(error.errorString);
  429.   return error;
  430. };
  431.  
  432.  
  433. searchvideoService.prototype.call =
  434. function searchvideoService_call(aListener, aMethod, aParams) {
  435.   var inst = this;
  436.   this._req = Cc['@mozilla.org/xmlextras/xmlhttprequest;1'].createInstance(Ci.nsIXMLHttpRequest);
  437.   this._req.onreadystatechange = function (aEvt) {
  438.     if(inst._req.readyState == 4) {
  439.       if (inst._req.status/100 == 2) {
  440.         var dom = inst._req.responseXML;
  441.         var domResponse = dom.getElementsByTagName("Response")[0];
  442.         if (domResponse.firstChild.nodeName == "Error") {
  443.           var error = inst.getError('SERVICE_ERROR', dom, null);
  444.           aListener.onError(error);
  445.         }
  446.         else
  447.           aListener.onResult(domResponse);
  448.       }
  449.       else if (inst._req.status/100 == 4) {
  450.         var dom = inst._req.responseXML;
  451.         var domResponse = dom.getElementsByTagName("Response")[0];
  452.         if (domResponse && (domResponse.firstChild.nodeName == "Error")) {
  453.           var error = inst.getError('SERVICE_ERROR', dom, null);
  454.           aListener.onError(error);
  455.         }
  456.         else {
  457.           // http errors
  458.           aListener.onError(inst.getError("HTTP_ERROR", null, inst._req.status));
  459.         }
  460.       }
  461.       else {
  462.         // http errors
  463.         aListener.onError(inst.getError("HTTP_ERROR", null, inst._req.status));
  464.       }
  465.     }
  466.   };
  467.   var body = "appid="+APPID+"&method="+aMethod;
  468.   for (var k in aParams) {
  469.     var v = aParams[k];
  470.     if (v == null || v == undefined)
  471.       v = "";
  472.     body += ("&"+k+"="+escape(v));
  473.   }
  474.   dump("JMC: searchvideo querystring is " + body + "\n");
  475.   this._req.open("GET", 'http://api.searchvideo.com/apiv3?'+body, true);
  476.   this._req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded", false);
  477.   rval = this._req.send(null); 
  478. }
  479.  
  480. // BEGIN flockIMediaWebService interface
  481. searchvideoService.prototype.decorateForMedia =
  482. function searchvideoService_decorateForMedia(aDocument) {
  483.   DEBUG("{flockIMediaWebService}.decorateForMedia(aDocument)");
  484.   var results = Cc["@mozilla.org/hash-property-bag;1"]
  485.                           .createInstance(Ci.nsIWritablePropertyBag2);
  486.   if (this.webDetective.detect("searchvideo", "person", aDocument, results)) {
  487.     var mediaArr = new Array();
  488.     var media = {
  489.       name: results.getPropertyAsAString("username"),
  490.       query: "user:" + results.getPropertyAsAString("userid"),
  491.       label: results.getPropertyAsAString("username"),
  492.       favicon: this.icon,
  493.       service: this.shortName
  494.     }
  495.     mediaArr.push(media);
  496.     if (!aDocument._flock_decorations) {
  497.       aDocument._flock_decorations = new Object();
  498.     }
  499.     aDocument._flock_decorations.mediaArr = mediaArr;
  500.     this.obs.notifyObservers(aDocument, "media", "media:update");
  501.   }
  502. }
  503. // END flockIMediaWebService
  504.  
  505. // ========== END searchvideoService class ==========
  506.  
  507.  
  508. // ================================================
  509. // ========== BEGIN XPCOM Module support ==========
  510. // ================================================
  511.  
  512. function createModule(aParams) {
  513.   var Cc = Components.classes;
  514.   var Ci = Components.interfaces;
  515.   var Cr = Components.results;
  516.   return {
  517.     registerSelf: function (aCompMgr, aFileSpec, aLocation, aType) {
  518.       aCompMgr.QueryInterface(Ci.nsIComponentRegistrar);
  519.       aCompMgr.registerFactoryLocation( aParams.CID, aParams.componentName,
  520.                                         aParams.contractID, aFileSpec,
  521.                                         aLocation, aType );
  522.       var catMgr = Cc["@mozilla.org/categorymanager;1"]
  523.                      .getService(Ci.nsICategoryManager);
  524.       catMgr.addCategoryEntry( "flock-startup", aParams.componentName,
  525.                                "service,"+aParams.contractID, true, true ); 
  526.       if (!aParams.categories) { aParams.categories = []; }
  527.       for (var i = 0; i < aParams.categories.length; i++) {
  528.         var cat = aParams.categories[i];
  529.         catMgr.addCategoryEntry( cat.category, cat.entry,
  530.                                  cat.value, true, true ); 
  531.       }
  532.     },
  533.     getClassObject: function (aCompMgr, aCID, aIID) {
  534.       if (!aCID.equals(aParams.CID)) { throw Cr.NS_ERROR_NO_INTERFACE; }
  535.       if (!aIID.equals(Ci.nsIFactory)) { throw Cr.NS_ERROR_NOT_IMPLEMENTED; }
  536.       return { // Factory
  537.         createInstance: function (aOuter, aIID) {
  538.           if (aOuter != null) { throw Cr.NS_ERROR_NO_AGGREGATION; }
  539.           var comp = new aParams.componentClass();
  540.           if (aParams.implementationFunc) { aParams.implementationFunc(comp); }
  541.           return comp.QueryInterface(aIID);
  542.         }
  543.       };
  544.     },
  545.     canUnload: function (aCompMgr) { return true; }
  546.   };
  547. }
  548.  
  549. // NS Module entrypoint
  550. function NSGetModule(aCompMgr, aFileSpec) {
  551.   return createModule({
  552.     componentClass: searchvideoService,
  553.     CID: SEARCHVIDEO_CID,
  554.     contractID: SEARCHVIDEO_CONTRACTID,
  555.     componentName: "Searchvideo JS Component",
  556.     implementationFunc: function (aComp) { getCompTK().addAllInterfaces(aComp); },
  557.     categories: [ // "flock-startup" is automagically added
  558.       { category: "flockMediaProvider", entry: CATEGORY_ENTRY_NAME, value: SEARCHVIDEO_CONTRACTID }
  559.     ]
  560.   });
  561. }
  562.  
  563. // ========== END XPCOM Module support ==========
  564.